﻿using System;
using System.IO;
using System.Linq;
using System.Xml;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Ink;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using DiscoveryLogic.UI.Layouts.ContentScreens;
using DiscoveryLogic.UI.Layouts.Controls;
using System.Windows.Browser;
using DiscoveryLogic.DataServices.DataSources;
using DiscoveryLogic.UI.Layouts.Converters;
using DiscoveryLogic.UI.Layouts.CustomEventArgs;
using System.Windows.Markup;

namespace DiscoveryLogic.UI.Layouts.Controls
{
    public partial class Aspect : UserControl
    {
        #region Private Variables

            // A reference to the top canvas element
            private readonly Canvas topCanvas;

            // A reference to the screen aspect is on
            private View360 contentScreen;

            // Standard scale for aspects
            private double standardScaleFactor = 1;

            // Has the aspect been loaded?
            private bool isAspectLoaded = false;

            // Model
            Account model;

        #endregion

        #region Constructors 

            public Aspect(string aspectName, View360 content)
            {
                InitializeComponent();

                // Initialize from XAML and get a reference to the top Canvas element
                System.IO.Stream s = this.GetType().Assembly.GetManifestResourceStream(aspectName);
                topCanvas = XamlReader.Load(new System.IO.StreamReader(s).ReadToEnd()) as Canvas;
                LayoutRoot.Children.Add(topCanvas);

                // Attach Handlers to aspect
                this.AttachHandlers();

                contentScreen = content;
            }

            public Aspect(string aspectName, double canvasLeft, double canvasTop, View360 content)
            {
                InitializeComponent();

                // Initialize from XAML and get a reference to the top Canvas element
                try
                {
                    System.IO.Stream s = this.GetType().Assembly.GetManifestResourceStream(aspectName);
                    topCanvas = XamlReader.Load(new System.IO.StreamReader(s).ReadToEnd()) as Canvas;
                    LayoutRoot.Children.Add(topCanvas);
                }
                catch (Exception ex)
                {
                    string err = ex.Message;
                }
                
                // Attach Handlers to aspect
                this.AttachHandlers();

                // Set the top and left property
                topCanvas.SetValue(Canvas.LeftProperty, canvasLeft);
                topCanvas.SetValue(Canvas.TopProperty, canvasTop);

                this.contentScreen = content;

                // Add loaded event
                this.Loaded += new RoutedEventHandler(Aspect_Loaded);
            }

        #endregion

        #region Get/Set Properties

            // Gets/sets the Height property of this aspect
            public virtual new double Height
            {
                get{ return topCanvas.Height; }
                set{
                    topCanvas.Height = value;
                    topCanvas.SetValue(Canvas.HeightProperty, topCanvas.Height);
                }
            }

            // Gets/sets the isFave property of this aspect
            private bool isFave;
            private double faveScale = 1.4;

            public bool isFavourite
            {
                get{ return isFave; }
                set{
                    isFave = value;

                    // Resize depending on favorite status
                    if (isFave){
                        this.Scale = faveScale;
                    }
                    else{
                        this.Scale = standardScaleFactor;
                    }
                }
            }
            
            // Gets/sets the leftPosition property of this aspect
            private double leftPosition;

            public double Left
            {
                get{
                    return Convert.ToDouble(topCanvas.GetValue(Canvas.LeftProperty));
                }
                set{
                    this.leftPosition = value;

                    // If aspect is loaded, show animation, otherwise just set it
                    if (isAspectLoaded){
                        
                        // Find Storyboard and start it
                        Storyboard sb = topCanvas.FindName("MoveAspectX") as Storyboard;
                        SplineDoubleKeyFrame animX = topCanvas.FindName("MoveAspectAnimX") as SplineDoubleKeyFrame;
                        animX.SetValue(SplineDoubleKeyFrame.ValueProperty, value);

                        try{
                            sb.Begin();
                        }
                        catch (Exception ex){
                            string message = ex.Message;
                        }
                    }
                    else{
                        topCanvas.SetValue(Canvas.LeftProperty, value);
                    }
                }
            }                     

            // Gets/sets the model property of this aspect
            public Account Model
            {
                get { return model; }
                set { 
                    model = value; 
                    
                    // Set the details text
                    SetDetailsText();
                }
            }

            // Gets/sets the owningList property of this aspect
            private UserControl owningList;

            public UserControl Owner
            {
                get{ return this.owningList; }
                set{ this.owningList = value; }
            }
            
            // Gets/sets the currentScaleFactor property of this aspect
            private double currentScaleFactor = 1;

            public double Scale
            {
                get{ return this.currentScaleFactor; }
                set{
                    this.currentScaleFactor = value;

                    // If aspect is loaded, show animation, otherwise just set it
                    if (isAspectLoaded){

                        // Find storyboard, set values and start it
                        Storyboard sb = topCanvas.FindName("AspectResize") as Storyboard;

                        SplineDoubleKeyFrame keyFrameX = topCanvas.FindName("AspectResizeKeyFrameX") as SplineDoubleKeyFrame;
                        SplineDoubleKeyFrame keyFrameY = topCanvas.FindName("AspectResizeKeyFrameY") as SplineDoubleKeyFrame;

                        keyFrameX.SetValue(SplineDoubleKeyFrame.ValueProperty, value);
                        keyFrameY.SetValue(SplineDoubleKeyFrame.ValueProperty, value);

                        sb.Begin();
                    }
                    else{

                        // Find the transform and update its value
                        ScaleTransform st = topCanvas.FindName("ScaleAspect") as ScaleTransform;
                        st.SetValue(ScaleTransform.ScaleXProperty, value);
                        st.SetValue(ScaleTransform.ScaleYProperty, value);
                    }
                }
            }        
            
            // Gets/sets the topPosition property of this aspect
            private double topPosition;

            public double Top
            {
                get{ return Convert.ToDouble(topCanvas.GetValue(Canvas.TopProperty)); }
                set{
                    this.topPosition = value;

                    // If aspect is loaded, show animation, otherwise just set it
                    if (isAspectLoaded){

                        // Find storyboard, set values and start it
                        Storyboard sb = topCanvas.FindName("MoveAspectY") as Storyboard;
                        SplineDoubleKeyFrame animY = topCanvas.FindName("MoveAspectAnimY") as SplineDoubleKeyFrame;
                        animY.SetValue(SplineDoubleKeyFrame.ValueProperty, value);

                        try{
                            sb.Begin();
                        }
                        catch (Exception ex){
                            string message = ex.Message;
                        }
                    }
                    else{
                        topCanvas.SetValue(Canvas.TopProperty, value);
                    }
                    
                }
            }

            // Gets/sets the Width property of this aspect
            public virtual new double Width
            {
                get{ return topCanvas.Width; }
                set{
                    topCanvas.Width = value;
                    topCanvas.SetValue(Canvas.WidthProperty, topCanvas.Width);
                }
            }

            // Gets/sets the ZIndex property of this aspect
            private int ZIndex;

            public int ZOrder
            {
                get{ return this.ZIndex; }
                set
                {
                    this.ZIndex = value;
                    this.SetValue(Canvas.ZIndexProperty, value);
                }
            }

            // Title text Opacity
            private int TitleTextOpac = 1;

            public int TitleTextOpacity
            {
                get{ return this.TitleTextOpac; }
                set{
                    this.TitleTextOpac = value;
                    TextBlock tb = topCanvas.FindName("TitleText") as TextBlock;
                    tb.Opacity = value;
                }
            }

            // Detail Text Line 1 Opacity
            private int DetailText1Opac = 1;

            public int DetailText1Opacity
            {
                get{ return this.DetailText1Opac; }
                set{
                    this.DetailText1Opac = value;
                    TextBlock tb = topCanvas.FindName("DetailsText1") as TextBlock;
                    tb.Opacity = value;
                }
            }

            // Detail Text Line 2 Opacity
            private int DetailText2Opac = 1;

            public int DetailText2Opacity
            {
                get { return this.DetailText2Opac; }
                set {
                    this.DetailText2Opac = value;
                    TextBlock tb = topCanvas.FindName("DetailsText2") as TextBlock;
                    tb.Opacity = value;
                }
            }

            // Detail Text Line 3 Opacity
            private int DetailText3Opac = 1;

            public int DetailText3Opacity
            {
                get{ return this.DetailText3Opac; }
                set{
                    this.DetailText3Opac = value;
                    TextBlock tb = topCanvas.FindName("DetailsText3") as TextBlock;
                    tb.Opacity = value;
                }
            }

            // Set size of detail text
            private int detailTextSize = 8;

            public int DetailTextSize
            {
                get { return detailTextSize; }
                set
                {
                    detailTextSize = value;
                    
                    // Set font size for all 3 lines of detail text
                    for (int i = 1; i < 4; i++)
                    {
                        ((TextBlock)topCanvas.FindName("DetailsText" + i)).FontSize = detailTextSize;
                    }
                    
                }
            }

            // Set size of aspect title text
            private int titleTextSize;

            public int TitleTextSize
            {
                get { return titleTextSize; }
                set 
                { 
                    titleTextSize = value;
                    ((TextBlock)topCanvas.FindName("TitleText")).FontSize = titleTextSize;
                }
            }

            

        #endregion

        #region Mouse Events

            // Variables for Mouse Drag and drop
            bool isMouseOver = false;
            bool isMouseDown = false;
            double mouseDownX;
            double mouseDownY;
            double originMouseX;
            double originMouseY;

            // Mouse enters the aspect
            protected void MouseEnterEvent(object o, EventArgs e)
            {
                isMouseOver = true;

                Canvas AspectCanvas = (Canvas)o;

                // Change cursor to hand
               // AspectCanvas.SetValue(FrameworkElement.CursorProperty, "Hand");
                AspectCanvas.Cursor = Cursors.Hand; 

                // Highlight the aspect
                Storyboard sb = topCanvas.FindName("AspectHighlight") as Storyboard;
                ColorAnimation animColor = topCanvas.FindName("AspectHighlightColor") as ColorAnimation;
                animColor.SetValue(ColorAnimation.ToProperty, Colors.Green);
                sb.Begin();
            }

            // Mouse leaves the aspect
            protected void MouseLeaveEvent(object o, EventArgs e)
            {
                isMouseOver = false;

                Canvas AspectCanvas = (Canvas)o;
                
                // Set cursor to arrow
                // AspectCanvas.SetValue(FrameworkElement.CursorProperty, "Arrow");
                AspectCanvas.Cursor = Cursors.Arrow; 

                // Un-highlight the aspect
                Storyboard sb = topCanvas.FindName("AspectHighlight") as Storyboard;
                ColorAnimation animColor = topCanvas.FindName("AspectHighlightColor") as ColorAnimation;
                animColor.SetValue(ColorAnimation.ToProperty, Colors.White);
                sb.Begin();
            }

            private int oldZOrder;
            // Mouse clicks on aspect
            protected void MouseDownEvent(object o, MouseEventArgs e)
            {
                isMouseDown = true;

                originMouseX = e.GetPosition(null).X;
                originMouseY = e.GetPosition(null).Y;

                // Used for detecting click
                // Reason is that originMouseX and Y are changed on drag
                mouseDownX = originMouseX;
                mouseDownY = originMouseY;

                Canvas AspectCanvas = (Canvas)o;
                AspectCanvas.CaptureMouse();

                // Raise Z order as item is picked up
                oldZOrder = this.ZOrder;
                this.ZOrder = 1000;

                // Hide text
                TitleTextOpacity = 0;
                DetailText1Opacity = 0;
                DetailText2Opacity = 0;
                DetailText3Opacity = 0;

            }

            // Mouse button is released
            protected void MouseUpEvent(object o, MouseEventArgs e)
            {
                isMouseDown = false;

                Canvas AspectCanvas = (Canvas)o;
                AspectCanvas.ReleaseMouseCapture();

                // Return aspect's Z order to normal
                this.ZOrder = oldZOrder;

                // Show text
                TitleTextOpacity = 100;
                DetailText1Opacity = 100;
                DetailText2Opacity = 100;
                DetailText3Opacity = 100;

                // Current mouse positions
                double currentMouseX = e.GetPosition(null).X;
                double currentMouseY = e.GetPosition(null).Y;

                if (isMouseOver
                    && Math.Abs(currentMouseX - mouseDownX) < 6
                    && Math.Abs(currentMouseY - mouseDownY) < 6)
                {
                    // It's a click
                    NavEventArgs args = new NavEventArgs();
                    args.Account = this.Model;
                    AspectClick(this, args);
                }
            }

            // Mouse is being moved
            protected void MouseMoveEvent(object o, MouseEventArgs e)
            {
                // If the mouse is down on an aspect
                if (isMouseDown)
                {
                    Canvas AspectCanvas = (Canvas)o;

                    // Retrieve current mouse position
                    double currentMouseX = e.GetPosition(null).X;
                    double currentMouseY = e.GetPosition(null).Y;

                    // Retrieve current Canvas left and top
                    double currentCanvasTop = Convert.ToDouble(AspectCanvas.GetValue(Canvas.TopProperty));
                    double currentCanvasLeft = Convert.ToDouble(AspectCanvas.GetValue(Canvas.LeftProperty));

                    // Reset the location of the object
                    AspectCanvas.SetValue(
                        Canvas.LeftProperty,
                        currentCanvasLeft + (currentMouseX - originMouseX)
                        );
                    AspectCanvas.SetValue(
                        Canvas.TopProperty,
                        currentCanvasTop + (currentMouseY - originMouseY)
                        );

                    // Update the beginning position of the mouse
                    originMouseX = currentMouseX;
                    originMouseY = currentMouseY;
                }
            }

        #endregion

        #region Helper Methods

            private void AttachHandlers()
            {
                topCanvas.MouseMove += new MouseEventHandler(MouseMoveEvent);
                Canvas iconBody = topCanvas.FindName("IconBody") as Canvas;
                if (iconBody != null)
                {
                    iconBody.MouseEnter += new MouseEventHandler(MouseEnterEvent);
                    iconBody.MouseLeave += new MouseEventHandler(MouseLeaveEvent);
                    iconBody.MouseLeftButtonDown += new MouseButtonEventHandler(MouseDownEvent);
                    iconBody.MouseLeftButtonUp += new MouseButtonEventHandler(MouseUpEvent);
                }
            }
            
            // Called when canvas has loaded
            // Used to set the isAspectLoaded flag
            private void Aspect_Loaded(object sender, EventArgs e)
            {
                isAspectLoaded = true;
            }

            // Called when the model property is assigned to
            // Used to set the details text beside aspect
            private void SetDetailsText()
            {
                // Find detail textblocks
                TextBlock title = topCanvas.FindName("TitleText") as TextBlock;
                TextBlock detail1 = topCanvas.FindName("DetailsText1") as TextBlock;
                TextBlock detail2 = topCanvas.FindName("DetailsText2") as TextBlock;
                TextBlock detail3 = topCanvas.FindName("DetailsText3") as TextBlock;

                // Set title
                title.Text = model.Name;

                // For calculating time until due date
                TimeSpan timeTillDue;

                // Set details depending on which account type
                switch (model.Type)
                {
                    case AccountType.Checking:
                        detail1.Text = "Balance:          " + model.Balance.ToCurrency();
                        detail2.Text = "Last Trans:      " + (-model.LastTransactionAmount).ToCurrency();
                        detail3.Text = "Trans. Date: " + model.LastTransactionDate.ToString("d MMM yy");
                        break;
                    case AccountType.Savings:
                        detail1.Text = "Balance:            " + model.Balance.ToCurrency();
                        detail2.Text = "";
                        detail3.Text = "";
                        break;
                    case AccountType.CreditCard:
                        detail1.Text = "Balance:             " + model.Balance.ToCurrency();
                        detail2.Text = "Next Payment:    " + model.NextPaymentAmount.ToCurrency();
                        detail3.Text = "Due Date:   " + model.NextPaymentDueDate.ToString("dd MMM yy");
                        
                        // Set colour of text depending on time until due
                        timeTillDue = model.NextPaymentDueDate - DateTime.Today;
                        if (timeTillDue.TotalDays < 6)
                        {
                            detail3.Foreground = new SolidColorBrush(Utils.HexToColor("#FF600000"));
                        }
                        else if (timeTillDue.TotalDays > 25)
                        {
                            detail3.Foreground = new SolidColorBrush(Utils.HexToColor("#FF488E48"));
                        }
                        break;
                    case AccountType.FiveTwoNine:
                        detail1.Text = "Balance:            " + model.Balance.ToCurrency();
                        detail2.Text = "";
                        detail3.Text = "";
                        break;
                    case AccountType.Investments:
                        detail1.Text = "Balance:            " + model.Balance.ToCurrency();
                        detail2.Text = "";
                        detail3.Text = "";
                        break;
                    case AccountType.Mortgage:
                        detail1.Text = "Balance:           " + model.Balance.ToCurrency();
                        detail2.Text = "Next Payment:  " + model.NextPaymentAmount.ToCurrency();
                        detail3.Text = "Due Date:   " + model.NextPaymentDueDate.ToString("dd MMM yy");

                        // Set colour of text depending on time until due
                        timeTillDue = model.NextPaymentDueDate - DateTime.Today;
                        if (timeTillDue.TotalDays < 6){
                            detail3.Foreground = new SolidColorBrush(Utils.HexToColor("#FF600000"));
                        }
                        else if (timeTillDue.TotalDays > 25){
                            detail3.Foreground = new SolidColorBrush(Utils.HexToColor("#FF488E48"));
                        }
                        break;
                    case AccountType.Plan401k:
                        detail1.Text = "Balance:            " + model.Balance.ToCurrency();
                        detail2.Text = "";
                        detail3.Text = "";
                        break;
                    case AccountType.TradeHistory:
                        detail1.Text = "Value:              " + model.Balance.ToCurrency();
                        detail2.Text = "";
                        detail3.Text = "";
                        break;
                    default:
                        detail1.Text = "";
                        detail2.Text = "";
                        detail3.Text = "";
                        break;
                }
            }

        #endregion

        #region Public Methods

            // Sets the position that the account details are displayed
            public void SetDetailsTextPosition(double left, double top){
                Canvas textposition = topCanvas.FindName("DetailsText") as Canvas;
                textposition.SetValue(Canvas.LeftProperty, left);
                textposition.SetValue(Canvas.TopProperty, top);
            }

        #endregion

        #region Events

            public event EventHandler AspectClick;

        #endregion

    }
}
